package controller;

import java.util.ArrayList;
import java.util.List;

import modelInterfaces.AbstractModelFactory;
import modelInterfaces.IEventType;
import modelInterfaces.ITest;
import pluginInterfaces.IMetric;
import pluginInterfaces.IRecommender;
import utils.Codes;
import utils.Messages;
import utils.ReadProperties;
import utils.Validations;
import viewInterfaces.AbstractViewFactory;
import viewInterfaces.IViewTestSetup;
import controllerInterfaces.IControllerTestsPanel;
import controllerInterfaces.IControllerTestSetup;
import dao.DAOException;
import dao.DAOFactory;

/**
 * Controller class that sets the values adding a test.
 * 
 * @author Andy Cullache
 */
public class ControllerTestSetup implements IControllerTestSetup{
	
	private IViewTestSetup view;
	private IControllerTestsPanel myController;
	private List<IRecommender> algList;
	private List<IMetric> metList;

	/**
	 * Default constructor
	 */
	public ControllerTestSetup(){
		this.view = AbstractViewFactory.getAbstractViewFactory().getViewTestSetup();
		this.view.setController(this);
	}
	
	/**
	* Sets the test panel controller as controller of this class.
	* 
	* @param controller      panel controller class test.
	*/
	public void setController(IControllerTestsPanel controller){
		this.myController = controller;
	}
	
	
	/**
	 * Show to user the view.
	 */
	public void showView()
	{
		this.view.render();
	}	
	
	

	/**
	* Returns the list of configurations of algorithms present in the current project.
	* 
	* @return      configuration list of algorithms.
	*/
	public List<IRecommender> getAlgList() {
		return algList;
	}
	
	/**
	* Establishing the list of configurations algorithms.
	* 
	* @param algList      list of configurations algorithms.
	*/
	public void setAlgorithmList(List<IRecommender> algList) {
		this.algList = algList;
	}

	/**
	* Returns the list of configurations of metrics.
	* 
	* @return      configuration list of metrics.
	*/
	public List<IMetric> getMetList() {
		return metList;
	}

	/**
	* Establishing the list of configurations metrics.
	* 
	* @param metList      list of configurations metrics.
	*/
	public void setMetricList(List<IMetric> metList) {
		this.metList = metList;
	}
	
	/**
	* Add a test to the list of test panel and the current project.
	* 
	* @param name      test name.
	* @param metrics      metric list of test to add.
	* @param alg      algorithm list of test to add.
	* @param eventType      event type of test to add (event schema).
	* @return      true if it can add the test.
	*/
	public boolean addTest(String name, List<IMetric> metrics, List<IRecommender> alg, int eventType){
		ITest test = AbstractModelFactory.getModelFactory().getTest();
		test.setEventType(eventType);
		test.setName(name);
		test.setMetrics(metrics);
		test.setAlgorithms(alg);
		
		if(!Validations.isNoEmptyTest(test)){
			view.renderError(Codes.errorAddEmptyTest, Messages.errorAddEmptyTest);
			return false;
		}
		if(!Validations.isValidAddTest(this.myController.getTests(),test)){
			view.renderError(Codes.errorAddExistTest, Messages.errorAddExistTest);
			return false;
		}
		else{
			this.myController.addTest(test);
			this.myController.updateTests();
			return true;
		}
	}
		
	/**
	* Edit a test from the list of test panel and the current project, the test edit as a parameter.
	* 
	* @param name      test name.
	* @param metrics      metric list of test to add.
	* @param alg      algorithm list of test to add.
	* @param eventType      event type of test to add (event schema).
	* @return      true if it can add the test.
	*/
	public boolean editTest(ITest test, String name, List<IMetric> metrics, List<IRecommender> alg, int eventType){

		ITest aux = AbstractModelFactory.getModelFactory().getTest();
		aux.setName(name);
		if(!test.getName().equals(name) && !Validations.isValidAddTest(this.myController.getTests(),aux)){
			view.renderError(Codes.errorAddExistTest, Messages.errorAddExistTest);
			return false;
		}	
			test.setEventType(eventType);
		    test.setName(name);
			test.setMetrics(metrics);
			test.setAlgorithms(alg);
			
			if(!Validations.isNoEmptyTest(test)){
				view.renderError(Codes.errorAddEmptyTest, Messages.errorAddEmptyTest);
				return false;
			}
			else{
				this.myController.loadTestsToView();
				this.myController.updateTests();
				return true;
			}
	}

	/**
	 * Sets the values of the parameter test that comes in a view for editing.
	 * 
	 * @param test      test to edit.
	 */
	@Override
	public void setTest(ITest test) {
		this.view.setTest(test);
	}

	/**
	 * Gives the list of event types that exist in the dataset.
	 * 
	 * @return      list of event types.
	 */
	public List<String> getEventTypes(){
		
		List<String> types = new ArrayList<String>();
		List<IEventType> eventTypes = new ArrayList<IEventType>();
		if (myController.getDBConnectedStatus() == false)
		{
			view.renderError(Codes.DBconnectionDoesntExist, Messages.DBconnectionDoesntExist);
		}
		else
		{
			try
			{
				eventTypes = DAOFactory.getDAOFactory().getEventTypeDAO().retrieveAll();
				for (IEventType eventT : eventTypes)
				{
					types.add(eventT.getName());
				}
			}
			catch (DAOException e)
			{
				e.printStackTrace();
				this.view.renderError(e.getCode(), e.getMessage());
			}
		}
		return types;
	}
	
	
	/**
	 * Invokes the method that reads the properties file that contains 
	 * the mapping between event type and metric type (error or classification).
	 */
	public void setupMetricMapping(){
		if(ReadProperties.errorTypes==null && ReadProperties.classificationTypes == null){
			ReadProperties.readMetricsTypes("config/metricMapping.props");
		}
	}
	
	/**
	 * Returns a list the event types that can be evaluated with error metric.
	 * 
	 * @return      IDs of the event types.
	 */
	public List<String> getErrorMetricsTypes(){
		List<String> types = new ArrayList<String>();
		for(String type : ReadProperties.errorTypes.split(",")){
			types.add(type);
		}
		return types;
	}
	
	/**
	 * Returns a list the event types that can be evaluated with classification metric.
	 * 
	 * @return      IDs of the event types.
	 */
	public List<String> getClassificationMetricsTypes(){
		List<String> types = new ArrayList<String>();
		String[] aux = ReadProperties.classificationTypes.split(",");
		for(String type : aux){
			types.add(type);
		}
		return types;
	}

}
